
function sort_number(a,b) {
	    return a - b;
}

function _processa_tempo(tempos, data, rodada) {
	var peer;
	for(var i=0; i<data.length; i++) {
		peer = data[i];
		if (peer.chegada.rodada == peer.virou_seed.rodada && peer.chegada.rodada != 0) {
			if (rodada == undefined || peer.chegada.rodada == rodada) {
				tempos.push(peer.virou_seed.tempo - peer.chegada.tempo);
			}
		}
	}
}

function estatistica(data) {
	var n = data.length;

	var media = 0;
	for(var i=0; i<n; i++) {
		media += data[i];
	}
	media = 1.0 * media / n;

	var variancia = 0;
	for(var i=0; i<n; i++) {
		variancia += (data[i] - media)*(data[i] - media);
	}
	variancia = 1.0 * variancia / (n - 1);

	var inf = media - 1.96*Math.sqrt(variancia / n);
	var sup = media + 1.96*Math.sqrt(variancia / n);

	var mediana;
	data.sort(sort_number);
	if (n%2 == 1) {
		mediana = data[(n+1)/2];
	} else {
		mediana = (data[n/2] + data[n/2+1])/2.0;
	}

	var q = 1.0/2;
	mediana_inf = n * q - 1.96 * Math.sqrt((n*q *(1-q)));
	mediana_sup = n * q + 1.96 * Math.sqrt((n*q *(1-q)));

	return { 
		inf: inf, 
		sup: sup,
		media: media, 
		mediana: mediana,
		mediana_inf: data[Math.round(mediana_inf)],
		mediana_sup: data[Math.round(mediana_sup)]
	};
}

function ic_tempo_download(simulador) {
	var tempos = [];
	_processa_tempo(tempos, simulador.seeds_ativos);
	_processa_tempo(tempos, simulador.seeds_inativos);

	return estatistica(tempos);
}

function _calcula_acc(simulador, intervalo, rodada) {
	var tempos = [];
	_processa_tempo(tempos, simulador.seeds_ativos, rodada);
	_processa_tempo(tempos, simulador.seeds_inativos, rodada);

	var t = 0;
	var dt = intervalo;
	var acc = [ 0 ];
	var idx = 0;
	tempos.sort(sort_number);
	for(var i=0; i<tempos.length; i++) {
		while(tempos[i] > t+dt) {
			acc.push(acc[idx]);
			t += dt;
			idx++;
		}
		acc[idx]++;
	}
	for(var i=0; i<acc.length; i++) {
		acc[i] = 100.0 * acc[i] / tempos.length;
	}
	return acc;
}

function tempo_download_rodadas(container, simulador, intervalo) {
	var acc = []
	for (var i=0; i<simulador.settings.nr_rodadas; i++) {
		acc.push({
			name: 'Rodada '+(i+1),
			pointInterval: intervalo,
			data: _calcula_acc(simulador, intervalo, i)
		});
	}
	var chart = new Highcharts.Chart({
		chart: {
			renderTo: container,
			defaultSeriesType: 'line'
		},
		title: {
			text: 'CDF Tempo de Download por Rodadas'
		},
		xAxis: {
			title: {
				text: 'Tempo'
			}
		},
		yAxis: {
			min: 0,
			max: 100,
			title: {
				text: 'Porcentagem (%)'
			}
		},
		tooltip: {
			formatter: function() {
				return this.series.name+'<br>P(X < '+this.x+') = '+this.y+' %';
			}
		},
		plotOptions: {
			line: {
				marker: {
					enabled: false,
					symbol: 'circle',
					radius: 2,
					states: {
						hover: {
							enabled: true
						}
					}
				}
			}
		},
		series: acc
	});
}

function tempo_download(container, simulador, intervalo) {
	var acc = _calcula_acc(simulador, intervalo);
	var chart = new Highcharts.Chart({
		chart: {
			renderTo: container,
			defaultSeriesType: 'area'
		},
		title: {
			text: 'CDF Tempo de Download'
		},
		legend: {
			enabled: false
		},
		xAxis: {
			title: {
				text: 'Tempo'
			}
		},
		yAxis: {
			min: 0,
			max: 100,
			title: {
				text: 'Porcentagem (%)'
			}
		},
		tooltip: {
			formatter: function() {
				return 'P(X < '+this.x+') = '+this.y+' %';
			}
		},
		plotOptions: {
			area: {
				marker: {
					enabled: false,
					symbol: 'circle',
					radius: 2,
					states: {
						hover: {
							enabled: true
						}
					}
				}
			}
		},
		series: [{
			pointInterval: intervalo,
			data: acc
		}]
	});
}

function tempo_download_per_second(profile) {
	var chart = new Highcharts.Chart({
		chart: {
			renderTo: 'tempo_download_per_second',
			zoomType: 'x',
			spacingRight: 20,
			defaultSeriesType: 'line'
		},
		title: {
			text: 'Tempo de download médio desde o início'
		},
		yAxis: {
			min: 0,
			startOnTick: false,
			showFirstLabel: false,
			title: {
				text: 'Tempo de Download Médio (tempo)'
			}
		},
		plotOptions: {
			line: {
				lineWidth: 1,
				marker: {
					enabled: false,
					states: {
						hover: {
							enabled: true,
							radius: 5
						}
					}
				},
				shadow: false,
				states: {
					hover: {
						lineWidth: 1
					}
				}
			}
		},
		legend: {
			enabled: false
		},
		tooltip: {
			formatter: function() {
				return ''+this.y +' ut ['+ this.x + ' ut]';
			}
		},
		series: [{
			pointInterval: profile.simulador.settings.dt,
			data: profile.tempo_download_medio
		}]
  });
}

function peers_seeds_per_second(profile) {
	var chart = new Highcharts.Chart({
		chart: {
			renderTo: 'peers_seeds_per_second',
			zoomType: 'x',
			spacingRight: 20,
			defaultSeriesType: 'line'
		},
		title: {
			text: 'Nr médio de peers desde o início'
		},
		yAxis: {
			min: 0,
			startOnTick: false,
			showFirstLabel: false,
			title: {
				text: 'Quantidade média'
			}
		},
		plotOptions: {
			line: {
				lineWidth: 1,
				marker: {
					enabled: false,
					states: {
						hover: {
							enabled: true,
							radius: 5
						}
					}
				},
				shadow: false,
				states: {
					hover: {
						lineWidth: 1
					}
				}
			}
		},
		legend: {
			enabled: false
		},
		tooltip: {
			formatter: function() {
				return ''+this.y +' '+ this.series.name +' ['+ this.x + ' ut]';
			}
		},
		series: [{
			pointInterval: profile.simulador.settings.dt,
			data: profile.nr_peers_medio
		}]
  });
}

function vazao(simulador) {
	var ret = [];
	for (var i=0; i<simulador.rodadas.length; i++) {
		ret.push(0);
	}
	var seed;
	for (var i=0; i<simulador.seeds_inativos.length; i++) {
		seed = simulador.seeds_inativos[i];
		ret[seed.saida.rodada]++;
	}
	for (var i=0; i<simulador.rodadas.length; i++) {
		ret[i] = 1.0 * ret[i] / (simulador.rodadas[i].tempo_fim - simulador.rodadas[i].tempo_inicio);
	}
	return ret;
}

function _hist_count(data) {
	// Descobri o maior e o menor elemento.
	menor = maior = data[0];
	for(var i=0; i<data.length; i++) {
		if (data[i] < menor) menor = data[i];
		if (data[i] > maior) maior = data[i];
	}

	// Cria vetor de contagem.
	var h = [];
	for(var i=menor; i<=maior; i++) {
		h.push(0);
	}

	for(var i=0; i<data.length; i++) {
		h[data[i]-menor]++;
	}

	var x = [];
	var y = [];
	for(var i=0; i<h.length; i++) {
		x.push(menor+i);
		y.push(100.0 * h[i] / data.length);
	}

	var total = 0;
	for(var i=0; i<y.length; i++) {
		total += y[i];
	}
	return [x, y];
}

function hist_nr_peers(data) {
	var tmp = _hist_count(data);
	var x = tmp[0];
	var y = tmp[1];
	var chart = new Highcharts.Chart({
		chart: {
			renderTo: 'hist_nr_peers',
			defaultSeriesType: 'column'
		},
		title: {
			text: 'Histograma do número de peers'
		},
		legend: {
			enabled: false
		},
		xAxis: {
			categories: x,
			title: {
				text: 'Peers'
			}
		},
		yAxis: {
			min: 0,
			title: {
				text: 'Percentual (%)'
			}
		},
		tooltip: {
			formatter: function() {
				return ''+this.x +' peer(s) ['+ this.y +' %]';
			}
		},
		plotOptions: {
			column: {
				borderWidth: 0
			}
		},
		series: [{
			data: y
		}]
  });
}

function gera_graficos_batch(simuladores) {
	var data = [];
	var categories = [];
	for (var i=0; i<simuladores.length; i++) {
		var n0 = simuladores[i].settings.n0;
		categories.push(n0);

		var v = vazao(simuladores[i]);
		var est = estatistica(v);
		data.push([ n0, est.inf]);
		data.push([ n0, est.media]);
		data.push([ n0, est.sup]);
	}
	var chart = new Highcharts.Chart({
		chart: {
			renderTo: 'vazao',
			defaultSeriesType: 'scatter',
			zoomType: 'xy'
		},
		title: {
			text: 'Vazão do sistema (Média e Intervalo de Confiança)'
		},
		xAxis: {
			title: {
				enabled: true,
				text: 'Peers (N0)'
			},
			startOnTick: true,
			showLastLabel: true,
			categories: categories
		},
		yAxis: {
			title: {
				text: 'Vazão (saídas/tempo)'
			}
		},
		tooltip: {
			formatter: function() {
				return 'Vazão: '+this.y+' ('+this.x+' peers)';
			}
		},
		legend: {
			enabled: false
		},
		plotOptions: {
			scatter: {
				marker: {
					radius: 5,
					states: {
						hover: {
							enabled: true,
							lineColor: 'rgb(100, 100, 100)'
						}
					}
				},
				states: {
					hover: {
						marker: {
							enabled: false
						}
					}
				}
			}
		},
		series: [{
			color: 'rgba(223, 83, 83, .5)',
			data: data
		}]
	});
	$("#estado").html("Concluído.");
}

function preenche_cenario(i) {
	settings = cenarios[i];
	$("#lambda").val(settings.lambda);
	$("#b").val(settings.b);
	$("#mi").val(settings.mi);
	$("#u").val(settings.u);
	$("#p").val(settings.p);
	$("#n0").val(settings.n0);
	$("#n0_fim").val(settings.n0_fim);
	if (settings.gamma == Simulador.GAMMA_INF) {
		$("#gamma").val('inf');
	} else {
		$("#gamma").val(settings.gamma);
	}
	$("#blocos_iniciais").val(settings.blocos_iniciais);
	$("#politica_escolha_bloco").val(settings.politica_escolha_bloco);
}

function run_from_params() {
	// Limpar os gráficos anteriores.
	$(".eh_grafico").html("");

	var lambda, b, mi, u, gamma, p, n0;
	var gamma_str = $("#gamma").val();
	if (gamma_str == "inf") {
		gamma = Simulador.GAMMA_INF;
	} else {
		gamma = parseFloat(gamma_str);
	}
	lambda = parseFloat($("#lambda").val());
	b = parseInt($("#b").val());
	mi = parseFloat($("#mi").val());
	u = parseFloat($("#u").val());
	p = parseFloat($("#p").val());
	n0 = parseInt($("#n0").val());
	n0_fim = n0;
	if ($("#n0_fim").val().trim() != "") {
		n0_fim = parseInt($("#n0_fim").val());
	}
	rodadas = parseInt($("#rodadas").val());
	qtd_eventos = parseInt($("#qtd_eventos").val());
	dt = parseFloat($("#dt").val());
	blocos_iniciais = parseInt($("#blocos_iniciais").val());
	politica = parseInt($("#politica_escolha_bloco :selected").val());

	var settings = {
		lambda: lambda,
		b: b,
		mi: mi,
		u: u,
		gamma: gamma,
		p: p,
		n0: n0,
		n0_fim: n0_fim,
		blocos_iniciais: blocos_iniciais,
		nr_rodadas: rodadas,
		qtde_eventos: qtd_eventos,
		tempo_fase_transiente: 1000,
		dt: dt,
		politica_escolha_bloco: politica
	};
	var errors = [];
	for (var key in settings) {
		if (isNaN(settings[key])) {
			errors.push(key + ": Valor inválido");
		}
	}
	if (errors.length > 0) {
		alert(errors.join("\n"));
		return;
	}
	$("#run").attr("disabled", "disabled");
	var n0_proximo = n0;
	var batch = (n0_fim > n0 ? true : false);
	var simuladores = [];
	function _run_n0() {
		if (simuladores.length <= n0_proximo && simulador_atual && simulador_atual.concluido) {
			simuladores.push(simulador_atual);
		}
		if (n0_proximo <= n0_fim && (simulador_atual == undefined || simulador_atual.concluido)) {
			console.log("Simulando: N0="+n0_proximo);
			$("#n0_atual").html(n0_proximo);
			var new_settings = $.extend({}, settings);
			new_settings.n0 = n0_proximo;
			run(new_settings, batch);
			n0_proximo++;
		}
		if (n0_proximo <= n0_fim || simulador_atual == undefined || !simulador_atual.concluido) {
			setTimeout(_run_n0, 500);
		} else {
			$("#run").removeAttr("disabled");
			gera_graficos_batch(simuladores);
		}
	}
	setTimeout(_run_n0, 300);
}

function get_tempo_fase_transiente_sistema_fechado(profile) {
	var tempo_transiente = 0;
	var convergiu = false;
	
	var epsilon = 0.01;
	var dt = profile.simulador.settings.dt;

	for(var i=1; i<profile.tempo_download_medio.length; i++) {
		if (!convergiu && (Math.abs(profile.tempo_download_medio[i] - profile.tempo_download_medio[i-1]) < epsilon * dt)) {
			tempo_transiente = profile.tempo[i];
			convergiu = true;
		}
	}
	if (!convergiu) {
		console.log("Não convergiu");
	}

	return tempo_transiente;
}

function get_tempo_fase_transiente_sistema_aberto(profile) {
	var tempo_transiente = 0;
	var convergiu = false;
	
	var epsilon = 0.01;
	var dt = profile.simulador.settings.dt;

	for(var i=1; i<profile.nr_peers_medio.length; i++) {
		if (!convergiu && (Math.abs(profile.nr_peers_medio[i] - profile.nr_peers_medio[i-1]) < epsilon * dt)) {
			tempo_transiente = profile.tempo[i];
			convergiu = true;
		}
	}
	if (!convergiu) {
		console.log("Não convergiu");
	}

	return tempo_transiente;
}

function get_tempo_fase_transiente(profile) {
	//Sistema fechado: p = 1 e lambda = 0
	if (profile.simulador.settings.p == 1 && profile.simulador.settings.lambda == 0) {
		return get_tempo_fase_transiente_sistema_fechado(profile);
	}
	return get_tempo_fase_transiente_sistema_aberto(profile);
}

function atualiza_estado() {
	$("#tempo_atual").html(simulador_atual.tempo_atual);
	$("#rodada_atual").html(simulador_atual.rodada_atual);
	$("#fila_eventos").html(simulador_atual.fila_eventos.length + " eventos");

	var total_tentativas_upload = simulador_atual.blocos_enviados + simulador_atual.blocos_nao_enviados_peer + simulador_atual.blocos_nao_enviados_bloco

	$("#total_tentativas_upload").html(total_tentativas_upload + " blocos");
	$("#uploads_sucesso").html(simulador_atual.blocos_enviados + " blocos");
	$("#uploads_sem_sucesso_peer").html(simulador_atual.blocos_nao_enviados_peer + " blocos");
	$("#uploads_sem_sucesso_bloco").html(simulador_atual.blocos_nao_enviados_bloco + " blocos");

	if (!simulador_atual.concluido) {
		setTimeout("atualiza_estado();", 500);
	}
}

function gerar_graficos(simulador) {
	var profile = simulador.profile;

	$("#estado").html("Gerando gráficos.");

	//histograma número de peers
	hist_nr_peers(profile.nr_peers);

	//Vazão (saídas por segundo)
	profile.vazao_por_rodada = vazao(simulador);
	var tmp = [];
	tmp.push("<b>Vazão (saídas/tempo)</b>");
	for (var i=0; i<profile.vazao_por_rodada.length; i++) {
		tmp.push("<b>Rodada "+i+"</b>: "+profile.vazao_por_rodada[i]);
	}
	$("#estat_vazao").html(tmp.join("<br>"));

	peers_seeds_per_second(profile);
	tempo_download('tempo_download', simulador, 0.3);
	tempo_download_rodadas('tempo_download_rodadas', simulador, 0.3);
	tempo_download_per_second(profile);

	var ic = ic_tempo_download(simulador);
	var tmp = [];
	tmp.push("<b>Tempo de download</b>");
	tmp.push("<b>Média:</b> "+ic.media);
	tmp.push("<b>IC 95%:</b> ["+ic.inf+", "+ic.sup+"]");
	tmp.push("<b>Mediana:</b> "+ic.mediana);
	tmp.push("<b>IC 95%:</b> ["+ic.mediana_inf+", "+ic.mediana_sup+"]");
	$("#estat_tempo_download").html(tmp.join("<br>"));

	$("#estado").html("Concluído.");

	return simulador;
}

var simulador_atual;
function run(settings, batch) {

	var simulador_transiente = new Simulador(settings);
	simulador_atual = simulador_transiente;
	
	simulador_transiente.prepare();

	$("#estado").html("Executando.");
	atualiza_estado();

	setTimeout(function() {
		//descobrindo fase transiente
		var nr_rodadas = settings.nr_rodadas;
		settings.nr_rodadas = 0;
		$("#estado").html("Descobrindo fase transiente.");
		simulador_transiente.run(function(simulador) {
			var profile = simulador_transiente.profile;

			settings.nr_rodadas = nr_rodadas;
			settings.tempo_fase_transiente = get_tempo_fase_transiente(profile);

			var simulador = new Simulador(settings);
			simulador_atual = simulador;

			$("#tempo_fase_transiente").html(simulador.settings.tempo_fase_transiente);

			//simulação considerando fase transiente
			simulador.prepare();

			$("#estado").html("Executando considerando fase transiente.");
			profile = simulador.run(function(simulador) {
				if (!batch) {
					gerar_graficos(simulador);
				}
			});
			console.log(profile);
		});
	}, 1);

}

